package parser;

import game.MoveExecutionList;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;

import org.xml.sax.SAXException;
import org.xml.sax.helpers.ParserAdapter;
import org.xml.sax.helpers.XMLReaderAdapter;

import chess.Board;
import chess.Player;

/**
 * This class will parse and write XML files for the chess game.
 * The format of the XML document is as follows:
 * <br>
 * <br>
 * < ChessGame > | < KnightsTourGame > | < EightsQueensGame >
 * <br><br>
 * &nbsp; &nbsp; < Command >
 * <br><br>
 * &nbsp; &nbsp; &nbsp; &nbsp; < CommandName >
 * <br><br>
 * &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; < Point type = "Origin">
 * <br><br>
 * &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; < X value = "Number">
 * &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; < / X >
 * <br><br>
 * &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; < Y value = "Number">
 * &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; < / Y >
 * <br><br>
 * &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; < / Point >
 * <br><br>
 * &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; < Point > | < Piece > | < NONE >
 * <br>
 * &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; < / Point > | < / Piece > | < / NONE >
 * <br><br>
 * &nbsp; &nbsp; &nbsp; &nbsp; < / CommandName >
 * <br>
 * &nbsp; &nbsp; < / Command > 
 * <br>
 * < / ChessGame >  | < / KnightsTourGame > | <  /EightsQueensGame >
 * @author Caleb
 *
 */
public class XMLParserAndReader extends ChessMoveParser{
	public static final String EXTENSION = "xml";
	private static final String DEFAULT_XML_TAG = "<?xml version='1.0' encoding='UTF-8'?>\n";
	
	private ParserAdapter adapter;
	public XMLParserAndReader(Board b, ArrayList<Player> players){
		super(b,players);
	}
	/**
	 * This method will parse the file. The format of the file must be of XML.
	 * If there is an error in creating the file, this method will return null.
	 * @return Returns the Move Execution List object that is created from the file
	 */
	@Override
	public MoveExecutionList parseFile() {
		if(file == null){
			return null;
		}
		ChessContentHandler handler = new ChessContentHandler(board,players);
		try {
			// The ParserAdaper was used instead of the XMLReaderAdapter because a
			// content handler could be set
			adapter = new ParserAdapter(new XMLReaderAdapter());
			adapter.setContentHandler(handler);
			adapter.parse(file.getPath());
			currentPlayer = handler.getCurrentPlayer();
			if(board.getGame() instanceof game.ChessGame){
				game.ChessGame game = (game.ChessGame)board.getGame();
				game.getClock().startNewTurn(currentPlayer);
			}
		} catch (SAXException e) {
			errorCode = handler.getErrorCode();
			//System.out.println("ERROR CODE : " + errorCode);
			handler.destroyList();
			return null;
		} catch (IOException e){
			errorCode = ParserErrorConstants.FILE_UNABLE_TO_OPEN;
			return null;
		}
		return handler.getMoveList();
	}
	/**
	 * This program will write an XML file for saving the state of the game. This method
	 * will return false if the file is not written correctly.
	 * @return Returns a true if the file has been successfully written
	 */
	@Override
	public boolean writeFile() {
		if(outputFile == null){
			return false;
		}
		FileWriter output = null;
		BufferedWriter writer = null;
		String xmlString = createWriteString();
		if(xmlString == null){
			errorCode = ParserErrorConstants.UNABLE_TO_CREATE_STRING_FILE_ERROR;
			return false;
		}
		try {
			output = new FileWriter(outputFile);
			writer = new BufferedWriter(output);
			writer.write(xmlString);
		} catch (IOException e){
			errorCode = ParserErrorConstants.FILE_IO_ERROR;
			return false;
		} finally{
			if(writer != null){
				try {
					writer.flush();
					writer.close();
					output.close();
					return true;
				} catch (IOException e) {
					errorCode = ParserErrorConstants.FILE_UNABLE_TO_CLOSE;
					return false;
				}
			}
		}
		return false;
	}
	/**
	 * This method will create the string to be written to the file. It creates a main
	 * tag that is game specific so when parsing, it will only read the files equal to the 
	 * current game's type
	 * @return Returns a string representation of the move list with the correct main tag
	 */
	private String createWriteString(){
		String s = DEFAULT_XML_TAG;
		String gameType = "";
		if(board.getGame() instanceof game.ChessGame){
			gameType = ChessContentHandlerConstants.MAIN_TAG_CHESS_GAME;
		}else if(board.getGame() instanceof game.KnightsTourGame){
			gameType = ChessContentHandlerConstants.MAIN_TAG_KNIGHTS_TOUR_GAME;
		}else if(board.getGame() instanceof game.EightQueensGame){
			gameType = ChessContentHandlerConstants.MAIN_TAG_EIGHTS_QUEENS_GAME;
		}
		if(gameType.equals("")){
			return null;
		}
		s += "<" + gameType + ">\n";
		s += "\t" + board.getGame().getMoveExecutionList().XMLSaveList().replaceAll("\n", "\n\t");
		s += "\n";
		s += "</" + gameType + ">\n";
		return s;
	}
	/**
	 * This method will set the extension of the files to be selected. For this class
	 * the file extension is .xml
	 */
	@Override
	public void setExtension() {
		chessExtension = EXTENSION;
	}
}
